﻿using DGZImp.Core.Configuration;
using DGZImp.Core.DbSqlSugar;
using DGZImp.Core.EFDbContext;
using DGZImp.Core.Exceptions;
using DGZImp.Core.Extensions;
using DGZImp.Core.Services;
using DGZImp.MdsProject.Devices;
using DGZImp.MdsProject.Devices.Entities;
using DGZImp.MdsProject.Devices.Models;
using DGZImp.MdsProject.DomainModels;
using DGZImp.MdsProject.Services;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using Nomad.Iot;
using SqlSugar;
using System;
using System.Threading.Tasks;

namespace DGZImp.MdsProject.Helper
{
    /// <summary>
    /// 镭神AGV任务辅助类
    /// 所有与AGV相关的辅助操作都放到此类中
    /// </summary>
    public static class LsAgvHelper
    {
        //镭神AGV的特殊控制

        //1.点控制
        //主要是转运车，长臂车，堆高车用
        //例如一个取放货任务A到B，取货点A，取货等待点A1，放货点B，放货等待点B1
        //AGV调度系统内需要针对A1和B1设置上报控制，AGV到达点位后会发送请求
        //所以一个取放货任务正常会有四次回调，A1取货前，A1取货后，B1放货前，B1放货后

        //2.路径控制
        //主要是密封车用
        //例如一个取放货任务A到B，取货点A，取货前点A1，放货点B，放货前点B1
        //AGV调度系统内需要针对A→A1和B→B1设置上报控制
        //所以一个取放货任务正常会有两次回调，到达A点要去A1前，到达B点要去B1前

        public static List<TaskCacheItem> TaskCacheList = new List<TaskCacheItem>();

        public static Mds_LogisticTaskService logisticSer = AutofacContainerModule.GetService<Mds_LogisticTaskService>();

        /// <summary>
        /// 等待点位配置
        /// Pos: 执行取放动作的位置
        /// WaitingPos: 等待点位
        /// </summary>
        public static List<WaitPositionItem> WaitingPositions
        {
            get
            {
                var lst = new List<WaitPositionItem>();
                AppSetting.Configuration.GetSection("LsAgvConfigs:WaitingPosMaps").Bind(lst);
                return lst;
            }
        }
        /// <summary>
        /// 点控制处理，主要是在等待状态下判断设备状态或者发送设备指令
        /// operation ： 0等待取货 1取货完成 2等待放货 3放货完成
        /// </summary>
        public static bool HandleAgvControl(RealDevice agv, string taskName, List<string> path, long? logisticTaskId,
            LsAgvOperationEnum operation, long? logisticTaskDetailId)
        {
            var msg = "";
            if (path.Count != 1)
            {
                msg = $"收到AGV路径控制请求{string.Join("->", path)}，但参数数量不对";
                SysLogger.LogError(msg);
                logisticSer.AddTaskLog(logisticTaskId, Models.LogMessageType.警告, msg).Wait();
                return false;
            }
            var pos = path[0];
            (msg, var chkRes) = operation switch
            {
                LsAgvOperationEnum.取货前检查 => ($"收到AGV点位{pos}控制请求，做取货前的检查", true),
                LsAgvOperationEnum.取货后处置 => ($"收到AGV点位{pos}控制请求，做取货后的处理", true),
                LsAgvOperationEnum.放货前检查 => ($"收到AGV点位{pos}控制请求，做放货前的检查", true),
                LsAgvOperationEnum.放货后处置 => ($"收到AGV点位{pos}控制请求，做放货后的处理", true),
                _ => ($"收到AGV点位{pos}控制请求，但类型{operation}不正确", false)
            };
            if (!chkRes)
            {
                SysLogger.LogError(msg);
                logisticSer.AddTaskLog(logisticTaskId, Models.LogMessageType.警告, msg).Wait();
                return false;
            }
            SysLogger.LogInformation(msg);

            var posInfo = GetLogisticSiteAliasInfo(agv.MachineId, null, pos);
            //var posInfo = WaitingPositions.FirstOrDefault(x => x.WaitingPos == pos);
            if (posInfo == null)
            {
                msg = $"收到AGV点位 {pos} 控制请求，但未找到配置项";
                SysLogger.LogError(msg);
                logisticSer.AddTaskLog(logisticTaskId, Models.LogMessageType.警告, msg).Wait();
                return false;
            }
            var device = GetPosBindRealDevice(Convert.ToInt64(agv.MachineId), posInfo.SiteCodeAlias);
            if (device == null)
            {
                msg = $"收到AGV点位 {pos} 控制请求，但未找到关联的数采设备实体";
                SysLogger.LogError(msg);
                logisticSer.AddTaskLog(logisticTaskId, Models.LogMessageType.警告, msg).Wait();
                return false;
            }
            chkRes = device.Type switch
            {
                RealDeviceTypeEmun.RGV => HandleAgvDockingRgv((Rgv)device.Device, operation, logisticTaskId, logisticTaskDetailId),
                RealDeviceTypeEmun.Hoister => HandleAgvDockingHoister((Hoister)device.Device, operation, logisticTaskId, logisticTaskDetailId),
                RealDeviceTypeEmun.Conveyor => HandleAgvDockingConveyor((Conveyor)device.Device, operation, pos, logisticTaskId),
                //混粉机以及烧结炉 点控完成后，通知设备关门或变为待机
                RealDeviceTypeEmun.PRD_HF => HandleAgvDockingProductor((Productor)device.Device, operation, logisticTaskId),
                RealDeviceTypeEmun.PRD_SJ => HandleAgvDockingProductor((Productor)device.Device, operation, logisticTaskId),
                //增加 下对接 取放料完成时，通知对接机构的逻辑
                RealDeviceTypeEmun.Connector_BOT => HandlerAgvDockingConnectorBot((Connector)device.Device, operation, logisticTaskId),
                _ => false
            };

            return chkRes;
        }
        /// <summary>
        /// 点控模式下：处理AGV对接RGV时的特殊检查和逻辑
        /// </summary>
        /// <param name="rgv"></param>
        /// <param name="operation"></param>
        /// <param name="logisticTaskId"></param>
        /// <returns></returns>
        private static bool HandleAgvDockingRgv(Rgv rgv, LsAgvOperationEnum operation, long? logisticTaskId, long? logisticTaskDetailId)
        {
            var msg = "";
            if (rgv.Data?.Status != RgvStatusEmun.空闲)
            {
                msg = $"RGV状态 {rgv.Data?.Status} 不是空闲状态，需等待";
                SysLogger.LogInformation(msg);
                logisticSer.AddTaskLog(logisticTaskId, Models.LogMessageType.提示, msg).Wait();
                return false;
            }
            if (rgv.Data?.CurPos != 99)
            {
                rgv.SendMoveTaskAsync(99).Wait();
                msg = $"RGV不在99位置，下发RGV移动任务，请等待";
                SysLogger.LogInformation(msg);
                logisticSer.AddTaskLog(logisticTaskId, Models.LogMessageType.提示, msg).Wait();
                return false;
            }
            if (operation == LsAgvOperationEnum.取货前检查 && rgv.Data?.IsFull != GoodsStatusEnum.有货)
            {
                msg = $"取货时RGV上无货";
                SysLogger.LogError(msg);
                logisticSer.AddTaskLog(logisticTaskId, Models.LogMessageType.错误, msg).Wait();
                return false;
            }
            if (operation == LsAgvOperationEnum.放货前检查 && rgv.Data?.IsFull != GoodsStatusEnum.无货)
            {
                msg = $"放货时RGV上有货";
                SysLogger.LogError(msg);
                logisticSer.AddTaskLog(logisticTaskId, Models.LogMessageType.错误, msg).Wait();
                return false;
            }
            if (operation == LsAgvOperationEnum.取货前检查 || operation == LsAgvOperationEnum.放货前检查)
            {
                var res = rgv.SendWorkingLockAsync().Result;
                if (!res.IsSuccess)
                {
                    msg = $"尝试发送RGV锁失败";
                    SysLogger.LogError(msg);
                    logisticSer.AddTaskLog(logisticTaskId, Models.LogMessageType.错误, msg).Wait();
                    return false;
                }
                msg = $"RGV已就位，继续取放货";
                SysLogger.LogInformation(msg);
                logisticSer.AddTaskLog(logisticTaskId, Models.LogMessageType.提示, msg).Wait();
                return true;
            }
            if (operation == LsAgvOperationEnum.取货后处置 || operation == LsAgvOperationEnum.放货后处置)
            {
                var res = rgv.SendWorkingUnlockAsync().Result;
                if (!res.IsSuccess)
                {
                    msg = $"尝试发送RGV解锁锁失败";
                    SysLogger.LogError(msg);
                    logisticSer.AddTaskLog(logisticTaskId, Models.LogMessageType.错误, msg).Wait();
                    return false;
                }
                msg = $" {operation} 已完成，释放RGV";
                SysLogger.LogInformation(msg);
                logisticSer.AddTaskLog(logisticTaskId, Models.LogMessageType.成功, msg).Wait();
                //放货完成后，可直接完成当前AGV任务，以便系统能自动下发RGV任务，确保任务并行
                if (operation == LsAgvOperationEnum.放货后处置)
                {
                    //完成AGV任务
                    logisticSer.FinishLogisticDetailTask(logisticTaskDetailId, null).Wait();

                    msg = $"完成AGV任务 {logisticTaskId}，确保RGV任务并行";
                    SysLogger.LogInformation(msg);
                    logisticSer.AddTaskLog(logisticTaskId, Models.LogMessageType.成功, msg).Wait();
                }
                return true;
            }

            return true;
        }
        /// <summary>
        /// 点控模式下：处理AGV对接提升机时的特殊检查和逻辑
        /// </summary>
        /// <param name="hoister"></param>
        /// <param name="operation"></param>
        /// <param name="logisticTaskId"></param>
        /// <returns></returns>
        private static bool HandleAgvDockingHoister(Hoister hoister, LsAgvOperationEnum operation, long? logisticTaskId, long? logisticTaskDetailId)
        {
            var msg = "";
            if (hoister.Data?.Status != HoistStatusEmun.空闲)
            {
                msg = $"提升机 {hoister.Data?.Status} 不是空闲状态，需等待";
                SysLogger.LogInformation(msg);
                logisticSer.AddTaskLog(logisticTaskId, Models.LogMessageType.提示, msg).Wait();
                return false;
            }
            if (hoister.Data?.CurPos != 1)
            {
                hoister.SendTaskAsync(hoister.Data.CurPos, 1).Wait();
                msg = $"提升机不在一层，需等待";
                SysLogger.LogInformation(msg);
                logisticSer.AddTaskLog(logisticTaskId, Models.LogMessageType.提示, msg).Wait();
                return false;
            }
            if (operation == LsAgvOperationEnum.取货前检查 && hoister.Data?.IsFull != GoodsStatusEnum.有货)
            {
                msg = $"取货时提升机上无货";
                SysLogger.LogError(msg);
                logisticSer.AddTaskLog(logisticTaskId, Models.LogMessageType.错误, msg).Wait();
                return false;
            }
            if (operation == LsAgvOperationEnum.放货前检查 && hoister.Data?.IsFull != GoodsStatusEnum.无货)
            {
                msg = $"放货时提升机上有货";
                SysLogger.LogError(msg);
                logisticSer.AddTaskLog(logisticTaskId, Models.LogMessageType.错误, msg).Wait();
                return false;
            }
            if (operation == LsAgvOperationEnum.取货前检查 || operation == LsAgvOperationEnum.放货前检查)
            {
                var res = hoister.SendWorkingLockAsync().Result;
                if (!res.IsSuccess)
                {
                    msg = $"尝试发送提升机锁失败，{res.Message}";
                    SysLogger.LogError(msg);
                    logisticSer.AddTaskLog(logisticTaskId, Models.LogMessageType.错误, msg).Wait();
                    return false;
                }
                SysLogger.LogInformation($"提升机已就位，继续取放货");
                return true;
            }
            if (operation == LsAgvOperationEnum.取货后处置 || operation == LsAgvOperationEnum.放货后处置)
            {
                var res = hoister.SendWorkingUnlockAsync().Result;
                if (!res.IsSuccess)
                {
                    msg = $"尝试解除提升机锁失败，{res.Message}";
                    SysLogger.LogError(msg);
                    logisticSer.AddTaskLog(logisticTaskId, Models.LogMessageType.错误, msg).Wait();
                    return false;
                }
                msg = $" {operation} 已完成，释放提升机";
                SysLogger.LogInformation(msg);
                logisticSer.AddTaskLog(logisticTaskId, Models.LogMessageType.成功, msg).Wait();
                //提升机放货完成后，可完成AGV任务，以便系统继续下发提升机任务，确保任务并行
                if (operation == LsAgvOperationEnum.放货后处置)
                {
                    //完成AGV任务
                    logisticSer.FinishLogisticDetailTask(logisticTaskDetailId, null).Wait();
                    msg = $"完成AGV任务，确保 提升机 任务并行";
                    SysLogger.LogInformation(msg);
                    logisticSer.AddTaskLog(logisticTaskId, Models.LogMessageType.成功, msg).Wait();
                }

                return true;
            }
            return true;
        }
        /// <summary>
        /// 点控模式下：处理AGV对接输送线时的特殊检查和逻辑
        /// </summary>
        /// <param name="conveyor"></param>
        /// <param name="operation"></param>
        /// <param name="pos"></param>
        /// <returns></returns>
        private static bool HandleAgvDockingConveyor(Conveyor conveyor, LsAgvOperationEnum operation, string pos, long? logisticTaskId)
        {
            var msg = "";
            if (conveyor.Data?.Status != ConveyorStatusEnum.空闲)
            {
                msg = $"当前AGV执行 {operation}， 设备 {conveyor.Name} 状态 {conveyor.Data.Status} 异常";
                SysLogger.LogError(msg);
                logisticSer.AddTaskLog(logisticTaskId, Models.LogMessageType.错误, msg).Wait();
                return false;
            }
            if (operation == LsAgvOperationEnum.取货前检查 || operation == LsAgvOperationEnum.放货前检查)
            {
                //写入空罐线 执行中 信号
                //if (conveyor.Data.Status != ConveyorStatusEnum.空闲)
                //{
                //    msg = $"收到AGV点位 {pos} 控制请求 请求 {operation}，但目标位置 不是空闲状态";
                //    SysLogger.LogError(msg);
                //    logisticSer.AddTaskLog(logisticTaskId, Models.LogMessageType.错误, msg).Wait();
                //    return false;
                //}
                //需判断当前位置是否有货
                //空罐线时，取货前 或 放货前 给设备确认信号
                if ((conveyor.Data.IsFull == GoodsStatusEnum.无货 && conveyor.Data.IsFullTwo == GoodsStatusEnum.无货)
                    && operation == LsAgvOperationEnum.取货前检查)
                {
                    msg = $"收到AGV点位 {pos} 控制请求 请求取货，但目标位置 无货";
                    SysLogger.LogError(msg);
                    logisticSer.AddTaskLog(logisticTaskId, Models.LogMessageType.错误, msg).Wait();
                    return false;
                }
                if ((conveyor.Data.IsFull == GoodsStatusEnum.有货 && conveyor.Data.IsFullTwo == GoodsStatusEnum.有货)
                    && operation == LsAgvOperationEnum.放货前检查)
                {
                    msg = $"收到AGV点位 {pos} 控制请求 请求放货，但目标位置 有货";
                    SysLogger.LogError(msg);
                    logisticSer.AddTaskLog(logisticTaskId, Models.LogMessageType.错误, msg).Wait();
                    return false;
                }
                conveyor.SendCommandAsync(ConveyorCommandEnum.取放罐进行中).Wait();
                SysLogger.LogInformation($"AGV正在执行取放任务中..");
            }
            // 测试暂时不需要固扫
            if (operation == LsAgvOperationEnum.放货后处置 && false)
            {
                var Db = AutofacContainerModule.GetService<ServiceDbContext>().SqlSugarClient;
                // 获取对应固扫
                MdsInit mdsInit = AutofacContainerModule.GetService<MdsInit>();
                var scanner = mdsInit.devices.FirstOrDefault(a => a.MachineId.ToString() == conveyor.Id && a.Type == RealDeviceTypeEmun.Scanner);
                if (scanner?.Device == null)
                {
                    msg = $"收到AGV点位 {pos} 控制请求 但是未找到输送线对应固定扫码器";
                    SysLogger.LogError(msg);
                    logisticSer.AddTaskLog(logisticTaskId, Models.LogMessageType.错误, msg).Wait();
                    return false;
                }
                try
                {
                    var realDevice = (Scanner)scanner.Device;
                    int i = 0;
                    string sn = string.Empty;
                    var startScanTime = DateTime.Now;
                    while (i++ < 3)
                    {
                        realDevice.SendOpenAsync().Wait();
                        Task.Delay(50).Wait();
                        if (string.IsNullOrEmpty(realDevice.Data.Barcode) || !(realDevice.Data.ScanTime >= startScanTime)) continue;
                        sn = realDevice.Data.Barcode;
                        break;
                    }
                    if (string.IsNullOrEmpty(sn))
                    {
                        msg = $"收到AGV点位 {pos} 控制请求 但是固扫未能成功扫码";
                        SysLogger.LogError(msg);
                        logisticSer.AddTaskLog(logisticTaskId, Models.LogMessageType.错误, msg).Wait();
                        return false;
                    }
                    // 获取重量
                    double weight = 0d;
                    i = 0;
                    while (i++ < 3)
                    {
                        if (conveyor.Data.Weight != 0)
                        {
                            weight = conveyor.Data.Weight;
                            break;
                        }
                        Task.Delay(50).Wait();
                    }

                    if (weight == 0)
                    {
                        msg = $"收到AGV点位 {pos} 控制请求 但是输送线未成功获取空桶重量";
                        SysLogger.LogError(msg);
                        logisticSer.AddTaskLog(logisticTaskId, Models.LogMessageType.错误, msg).Wait();
                        return false;
                    }

                    var container = Db.Set<Wms_Container>()
                        .Where(a => a.ContainerCode == sn && a.Enabled == 1 && a.ContainerType == 1)
                        .FirstOrDefault();
                    if (container == null)
                    {
                        msg = $"收到AGV点位 {pos} 控制请求 但是未找到容器编码{sn}对应数据";
                        SysLogger.LogError(msg);
                        logisticSer.AddTaskLog(logisticTaskId, Models.LogMessageType.错误, msg).Wait();
                        return false;
                    }

                    container.Weight = (decimal)weight;
                    Db.DoUpdate(container);
                }
                catch (Exception e)
                {
                    SysLogger.LogError(e, $"收到AGV点位 {pos} 控制请求 但是扫码/称重失败");
                    msg = $"收到AGV点位 {pos} 控制请求 但是扫码/称重失败 异常";
                    logisticSer.AddTaskLog(logisticTaskId, Models.LogMessageType.错误, msg).Wait();
                    return false;
                }
            }
            return true;
        }
        /// <summary>
        /// 点控模式下：处理AGV对接生产设备时的特殊检查和逻辑
        /// </summary>
        /// <param name="productor"></param>
        /// <param name="operation"></param>
        /// <returns></returns>
        private static bool HandleAgvDockingProductor(Productor productor, LsAgvOperationEnum operation, long? logisticTaskId)
        {
            var msg = "";
            SysLogger.LogInformation($"收到AGV点位控制请求，请求设备：{productor.Type}/{productor.Name}");
            if (productor.Data.Remote != ProductorRemoteEnum.远程联控)
            {
                msg = $"当前需要取放货的设备 {productor.Name} 不在远程联控状态";
                SysLogger.LogError(msg);
                logisticSer.AddTaskLog(logisticTaskId, Models.LogMessageType.错误, msg).Wait();
                return false;
            }
            if (operation == LsAgvOperationEnum.取货前检查)
            {
                //检查目标设备是否开门状态
                if (productor.Data.Status == ProductorStatusEmun.生产结束) return true;
                msg = $"当前需要取货的设备 {productor.Name} 状态 {productor.Data.Status},非 生产结束，不满足取货条件";
                SysLogger.LogError(msg);
                logisticSer.AddTaskLog(logisticTaskId, Models.LogMessageType.错误, msg).Wait();
                return false;
            }
            if (operation == LsAgvOperationEnum.放货前检查)
            {
                //检查目标设备是否开门状态
                if (productor.Data.Status == ProductorStatusEmun.设备待机) return true;
                msg = $"当前需要放货的设备 {productor.Name} 状态 {productor.Data.Status}，非 设备待机，不满足放货条件";
                SysLogger.LogError(msg);
                logisticSer.AddTaskLog(logisticTaskId, Models.LogMessageType.错误, msg).Wait();
                return false;
            }
            if (operation == LsAgvOperationEnum.取货后处置 || operation == LsAgvOperationEnum.放货后处置)
            {
                //放货后通知设备关门
                if (operation == LsAgvOperationEnum.取货后处置)
                {
                    productor.SendCommandAsync(ProductorCommandEmun.取货完成, "", "").Wait();
                }
                else
                {
                    var productTaskCode = GetProductTaskCode(logisticTaskId).Result;
                    productor.SendCommandAsync(ProductorCommandEmun.放货完成, "", productTaskCode).Wait();
                }
                msg = $"AGV 执行动作 {operation} 完成，通知设备 {productor.Code}/{productor.Name} 结束";
                SysLogger.LogInformation(msg);
                logisticSer.AddTaskLog(logisticTaskId, Models.LogMessageType.成功, msg).Wait();
                return true;
            }
            return true;
        }
        /// <summary>
        /// 点控模式下：处理AGV对接下对接机构的特殊检查和逻辑
        /// </summary>
        /// <param name="device"></param>
        /// <param name="operation"></param>
        /// <returns></returns>
        private static bool HandlerAgvDockingConnectorBot(Connector device, LsAgvOperationEnum operation, long? logisticTaskId)
        {
            var msg = "";
            var i = 0;
            if (device.Data?.Status != ConnectorStatus.空闲)
            {
                msg = $"当前AGV执行 {operation}， 设备 {device.Name} 状态 {device.Data.Status} 异常";
                SysLogger.LogError(msg);
                logisticSer.AddTaskLog(logisticTaskId, Models.LogMessageType.错误, msg).Wait();
                return false;
            }
            switch (operation)
            {
                case LsAgvOperationEnum.取货前检查:

                    if (device.Data?.IsFull == GoodsStatusEnum.无货)
                    {
                        msg = $"当前AGV执行 {operation}， 设备 {device.Name} 无货";
                        SysLogger.LogError(msg);
                        logisticSer.AddTaskLog(logisticTaskId, Models.LogMessageType.错误, msg).Wait();
                        return false;
                    }
                    break;
                case LsAgvOperationEnum.放货前检查:
                    if (device.Data?.IsFull == GoodsStatusEnum.有货)
                    {
                        msg = $"当前AGV执行 {operation}，设备 {device.Name} 有货";
                        SysLogger.LogError(msg);
                        logisticSer.AddTaskLog(logisticTaskId, Models.LogMessageType.错误, msg).Wait();
                        return false;
                    }
                    break;
                case LsAgvOperationEnum.取货后处置:
                    //下对接取货完成后，通知对接机构
                    //循环下发5次
                    while (i < 5)
                    {
                        var res = device.SendCommandAsync(ConnectorCommandEnum.取货完成).Result;
                        if (res.IsSuccess) break;
                        if (!res.IsSuccess)
                        {
                            msg = $"当前AGV执行 {operation},{device.Name}/{operation} 失败:" + res.Message;
                            SysLogger.LogError(msg);
                            logisticSer.AddTaskLog(logisticTaskId, Models.LogMessageType.错误, msg).Wait();
                        }
                        i++;
                        Thread.Sleep(500);
                    }
                    msg = $"AGV 执行动作 {operation} 完成，通知设备 {device.Name} 取货完成";
                    SysLogger.LogInformation(msg);
                    logisticSer.AddTaskLog(logisticTaskId, Models.LogMessageType.成功, msg).Wait();
                    break;
                case LsAgvOperationEnum.放货后处置:
                    //下对接放货完成后，通知对接机构完成
                    while (i < 5)
                    {
                        var tmpRes = device.SendCommandAsync(ConnectorCommandEnum.放货完成).Result;
                        if (!tmpRes.IsSuccess)
                        {
                            msg = $"当前AGV执行 {operation},{device.Name}/{operation} 失败:" + tmpRes.Message;
                            SysLogger.LogError(msg);
                            logisticSer.AddTaskLog(logisticTaskId, Models.LogMessageType.错误, msg).Wait();
                        }
                        if (tmpRes.IsSuccess) break;
                        i++;
                    }
                    msg = $"AGV 执行动作 {operation} 完成，通知设备 {device.Name} 放货完成";
                    SysLogger.LogInformation(msg);
                    logisticSer.AddTaskLog(logisticTaskId, Models.LogMessageType.成功, msg).Wait();
                    break;
                default:
                    msg = $"收到AGV点位控制请求 {operation}，请求设备 {device.Name} 不支持该操作";
                    SysLogger.LogError(msg);
                    logisticSer.AddTaskLog(logisticTaskId, Models.LogMessageType.错误, msg).Wait();
                    return false;
            }
            return true;
        }
        /// <summary>
        /// 路径控制处理，主要是在等待状态下判断设备状态或者发送设备指令
        /// </summary>
        public static bool HandleAgvControl(RealDevice agv, string taskName, List<string> path, long? logisticTaskId)
        {
            var msg = "";
            if (path.Count != 2)
            {
                msg = $"收到AGV路径控制请求{string.Join("->", path)}，但参数数量不对";
                SysLogger.LogError(msg);
                logisticSer.AddTaskLog(logisticTaskId, Models.LogMessageType.错误, msg).Wait();
                return false;
            }
            var posInfo = GetLogisticSiteAliasInfo(agv.MachineId, path[0], path[1]);
            //var posInfo = WaitingPositions.FirstOrDefault(x => x.Pos == path[0] && x.WaitingPos == path[1]);
            if (posInfo == null)
            {
                msg = $"收到AGV路径控制请求{path[0]}->{path[1]}，但未找到配置项";
                SysLogger.LogError(msg);
                logisticSer.AddTaskLog(logisticTaskId, Models.LogMessageType.错误, msg).Wait();
                return false;
            }
            SysLogger.LogInformation($"收到AGV路径控制请求，车辆已到达点位{path[0]}");
            var device = GetPosBindRealDevice(agv.MachineId, posInfo.SiteCodeAlias);
            if (device == null)
            {
                msg = $"收到AGV路径控制请求{path[0]}->{path[1]}，但未找到关联的数采设备实体";
                SysLogger.LogError(msg);
                logisticSer.AddTaskLog(logisticTaskId, Models.LogMessageType.错误, msg).Wait();
                return false;
            }
            if (device.Type == RealDeviceTypeEmun.PRD_SJ || device.Type == RealDeviceTypeEmun.PRD_YX)
            {
                var taskItem = GetTaskCache(Convert.ToInt32(taskName), ((Productor)device.Device).Code);
                // 对接中，等待
                if (taskItem.Processing == 1)
                    return false;

                // 对接完成，可以走了
                if (taskItem.Processing == 2)
                    return true;

                if (taskItem.Processing == 0)
                {
                    //密封舱：烧结或压型时，取密封车设备SCADA信息变量，同步下发密封车开关门信号
                    //需要通过SCADA视图的设备编号取的密封车的设备信息
                    var requestDevice = agv.Type == RealDeviceTypeEmun.AGV_MF ? agv.MachineCode : null;
                    // 给设备发送对接指令，发送成功后，将任务状态置为1
                    var productTaskCode = GetProductTaskCode(logisticTaskId).Result;
                    var res = ((Productor)device.Device).SendCommandAsync(
                         device.Type == RealDeviceTypeEmun.PRD_SJ ?
                         ProductorCommandEmun.放货到位 : ProductorCommandEmun.取货到位, requestDevice, productTaskCode).Result;
                    if (res.IsSuccess)
                    {
                        taskItem.Processing = 1;
                    }
                    else
                    {
                        msg = $"收到AGV路径控制请求{path[0]}->{path[1]}，发送设备对接指令时出现错误，{res.Message}";
                        SysLogger.LogError(msg);
                        logisticSer.AddTaskLog(logisticTaskId, Models.LogMessageType.错误, msg).Wait();
                    }
                    return false;
                }
            }
            else
            {
                msg = $"收到AGV路径控制请求{path[0]}->{path[1]}，但关联的数采设备类型不支持";
                SysLogger.LogError(msg);
                logisticSer.AddTaskLog(logisticTaskId, Models.LogMessageType.错误, msg).Wait();
                return false;
            }
            TaskCacheList.RemoveAll(x => x.CreateTime < DateTime.Now.AddDays(-7));
            return false;
        }

        /// <summary>
        /// 生成任务数据; 任务类型：F01:搬运 F02:指定点 F03:充电 F04:取货 F05:卸货
        /// </summary>
        /// <param name="command"></param>
        /// <param name="taskNo"></param>
        /// <param name="start"></param>
        /// <param name="end"></param>
        /// <returns></returns>
        public static object GenerateTaskData(CreateLsAgvTaskDto data)
        {
            var command = data.Command;
            var taskNo = data.TaskNo;
            var deviceIp = data.DeviceIp;
            var start = data.Start;
            var end = data.End;
            var deviceType = data.DeviceType;

            //依据IP查找当前设备信息
            MdsInit mdsInit = AutofacContainerModule.GetService<MdsInit>();
            var agv = mdsInit.devices.Where(x => x.Device is LsAgv)
               .Where(x => ((LsAgv)x.Device).Data.AgvIp == deviceIp).FirstOrDefault();
            var agvMachineId = agv != null ? agv.MachineId : null;

            var taskName = taskNo.ToString();

            if (command == AgvCommandEmun.取消任务)
            {
                var req = new HttpReq<modifyTaskData>
                {
                    reqCode = SimpleSnowId.GenerateId(),
                    type = 1,
                    data = new modifyTaskData
                    {
                        taskname = taskName,
                        // 1:任务暂停执行 2:任务取消 3:任务恢复执行 4:对异常任务重新执行
                        action = 2
                    }
                };
                return req;
            }
            else
            {
                var reqTask = new HttpReq<addTaskData>
                {
                    reqCode = SimpleSnowId.GenerateId(),
                    type = 1,
                    data = new addTaskData
                    {
                        key = new keyData { ischain = false, custom_focal = false },
                        value = new List<valueDataItem>()
                    }
                };
                if (command == AgvCommandEmun.移动任务)
                {
                    reqTask.data.value.Add(new valueDataItem
                    {
                        taskname = taskName,
                        task_type = "F02",
                        index = 1,
                        priority = 4,
                        deviceid = deviceIp,
                        device_type = deviceType.ToString(),
                        deslocation = end,
                    });
                    return reqTask;
                }
                if (command == AgvCommandEmun.取货任务)
                {
                    var waiting = GetLogisticSiteAliasInfo(agvMachineId, start);
                    var taskIndex = reqTask.data.value.Count + 1;
                    reqTask.data.value.Add(new valueDataItem
                    {
                        taskname = waiting == null ? taskName : taskName + "-" + taskIndex.ToString(),
                        task_type = deviceType == RealDeviceTypeEmun.AGV_MF ? "F02" : "F04",
                        index = taskIndex,
                        priority = 4,
                        deviceid = deviceIp,
                        device_type = deviceType.ToString(),
                        getlocation = deviceType == RealDeviceTypeEmun.AGV_MF ? "" : start,
                        deslocation = deviceType == RealDeviceTypeEmun.AGV_MF ? start : ""
                    });
                    if (waiting != null && waiting.ConfirmSiteCode != null)
                    {
                        taskIndex = reqTask.data.value.Count + 1;
                        reqTask.data.key.ischain = true;
                        reqTask.data.value.Add(new valueDataItem
                        {
                            taskname = taskName + "-" + taskIndex.ToString(),
                            task_type = "F02",
                            index = taskIndex,
                            priority = 4,
                            deviceid = deviceIp,
                            device_type = deviceType.ToString(),
                            deslocation = waiting.ConfirmSiteCode,
                        });
                    }
                    return reqTask;
                }
                if (command == AgvCommandEmun.放货任务)
                {
                    var waiting = GetLogisticSiteAliasInfo(agvMachineId, end);
                    var taskIndex = reqTask.data.value.Count + 1;
                    reqTask.data.value.Add(new valueDataItem
                    {
                        taskname = waiting == null ? taskName : taskName + "-" + taskIndex.ToString(),
                        task_type = deviceType == RealDeviceTypeEmun.AGV_MF ? "F02" : "F05",
                        index = taskIndex,
                        priority = 4,
                        deviceid = deviceIp,
                        device_type = deviceType.ToString(),
                        putlocation = deviceType == RealDeviceTypeEmun.AGV_MF ? "" : end,
                        deslocation = deviceType == RealDeviceTypeEmun.AGV_MF ? end : ""
                    });
                    if (waiting != null && waiting.ConfirmSiteCode != null)
                    {
                        taskIndex = reqTask.data.value.Count + 1;
                        reqTask.data.key.ischain = true;
                        reqTask.data.value.Add(new valueDataItem
                        {
                            taskname = taskName + "-" + taskIndex.ToString(),
                            task_type = "F02",
                            index = taskIndex,
                            priority = 4,
                            deviceid = deviceIp,
                            device_type = deviceType.ToString(),
                            deslocation = waiting.ConfirmSiteCode,
                        });
                    }
                    return reqTask;
                }
                if (command == AgvCommandEmun.取放货任务)
                {
                    //var waiting = WaitingPositions.FirstOrDefault(x => x.Pos == end);
                    //var waitingStart = GetLogisticSiteAliasInfo(agvMachineId, start);
                    var waitingEnd = GetLogisticSiteAliasInfo(agvMachineId, end);
                    var taskIndex = reqTask.data.value.Count + 1;
                    //起点和终点的确认点都为空时，直接取放货
                    if (waitingEnd == null)
                    {
                        reqTask.data.value.Add(new valueDataItem
                        {
                            taskname = taskNo.ToString(),
                            task_type = "F01",
                            index = taskIndex,
                            priority = 4,
                            deviceid = deviceIp,
                            device_type = deviceType.ToString(),
                            getlocation = start,
                            putlocation = end,
                        });
                        return reqTask;
                    }
                    taskIndex = reqTask.data.value.Count + 1;
                    reqTask.data.key.ischain = true;
                    //起点取货任务
                    //密封车：依据路线控制，下发路线指定点起点以及指定点终点，后退出到终点待命点
                    //其他车型：下发起点到终点任务，以及退出 移动到终点确认点 则整个任务完成
                    reqTask.data.value.Add(new valueDataItem
                    {
                        taskname = taskName + "-" + taskIndex,
                        task_type = deviceType == RealDeviceTypeEmun.AGV_MF ? "F02" : "F01",
                        index = taskIndex,
                        priority = 4,
                        deviceid = deviceIp,
                        device_type = deviceType.ToString(),
                        deslocation = deviceType == RealDeviceTypeEmun.AGV_MF ? start : null,
                        getlocation = deviceType == RealDeviceTypeEmun.AGV_MF ? null : start,
                        putlocation = deviceType == RealDeviceTypeEmun.AGV_MF ? null : end,
                    });
                    //取放货时，需检查任务开始确认点以及结束确认点是否有任务
                    if (deviceType == RealDeviceTypeEmun.AGV_MF)
                    {
                        //终点放货任务
                        taskIndex = reqTask.data.value.Count + 1;
                        reqTask.data.value.Add(new valueDataItem
                        {
                            taskname = taskName + "-" + taskIndex,
                            task_type = "F02",
                            index = taskIndex,
                            priority = 4,
                            deviceid = deviceIp,
                            device_type = deviceType.ToString(),
                            deslocation = end,
                        });
                    }
                    //判断是否终点确认
                    if (waitingEnd != null && waitingEnd.ConfirmSiteCode != null)
                    {
                        taskIndex = reqTask.data.value.Count + 1;
                        reqTask.data.value.Add(new valueDataItem
                        {
                            taskname = taskName + "-" + taskIndex,
                            task_type = "F02",
                            index = taskIndex,
                            priority = 4,
                            deviceid = deviceIp,
                            device_type = deviceType.ToString(),
                            deslocation = waitingEnd.ConfirmSiteCode,
                        });
                    }

                    return reqTask;
                }
                else
                {
                    throw new AppException("不支持的AGV指令");
                }
            }
        }

        /// <summary>
        /// 当AGV任务完成后，做一些特殊处理，例如通知设备上料完成或者下料完成
        /// 目前上料完成需要通知的有混粉机，取料完成需要通知的是混粉机和烧结炉
        /// 依据返回路径，是否为确认点，若是，则回复设备完成，如不是，则等任务完成再回复
        /// </summary>
        public static async Task HandleTaskComplete(Mds_LogisticTaskDetail task, LsAgvReqTypeEnum agvReqType, List<string> path)
        {
            if (task == null) return;
            if (agvReqType != LsAgvReqTypeEnum.任务完成) return;
            if (path == null || path.Count == 0) return;
            var siteCode = path[0];
            if (string.IsNullOrEmpty(siteCode)) return;
            var msg = "";
            var tmpDevice = GetPosBindRealDevice(task.MachineId, task.EndSiteAlias);
            if (tmpDevice == null) return;

            if (tmpDevice.Type != RealDeviceTypeEmun.PRD_HF) return;
            //RealDevice tmpDevice = null;
            var confirmSite = GetLogisticSiteAliasInfo(task.MachineId, task.EndSiteAlias);

            //if (agvReqType == LsAgvReqTypeEnum.取货完成)
            //{
            //    confirmSite = GetLogisticSiteAliasInfo(task.MachineId, task.BeginSiteAlias);
            //    tmpDevice = GetPosBindRealDevice(task.MachineId, task.BeginSiteAlias);
            //}
            //if (agvReqType == LsAgvReqTypeEnum.任务完成)
            //{
            //    confirmSite = GetLogisticSiteAliasInfo(task.MachineId, task.EndSiteAlias);
            //    tmpDevice = GetPosBindRealDevice(task.MachineId, task.EndSiteAlias);
            //}
            //取料完成或上料完成，需要传递 生产任务的编号信息
            var productTaskCode = await GetProductTaskCode((long)task.LogisticTaskId);

            //需检查当前设备 点位是否配置 确认点，如有，则忽略
            if (confirmSite != null && !string.IsNullOrEmpty(confirmSite.ConfirmSiteCode) &&
                !siteCode.Equals(confirmSite.ConfirmSiteCode))
            {
                //判断当前位置是否 确认点
                msg = $"当前路线配置了 AGV 点控 确认点，{agvReqType} 当前 AGV位置 {siteCode} 非 确认点，系统忽略AGV回调。";
                SysLogger.LogInformation(msg);
                await logisticSer.AddTaskLog(task.LogisticTaskId, Models.LogMessageType.提示, msg);
                return;
            }
            //取料完成,通知下序设备
            var i = 1;
            OperateResult res = null;
            while (i <= 5)
            {
                i++;
                //if (agvReqType == LsAgvReqTypeEnum.取货完成 && (tmpDevice.Type == RealDeviceTypeEmun.PRD_HF || tmpDevice.Type == RealDeviceTypeEmun.PRD_SJ))
                //    res = await ((Productor)tmpDevice.Device).SendCommandAsync(ProductorCommandEmun.取货完成, null, "");
                //上料完成
                //if (agvReqType == LsAgvReqTypeEnum.任务完成 && tmpDevice.Type == RealDeviceTypeEmun.PRD_HF)
                res = await ((Productor)tmpDevice.Device).SendCommandAsync(ProductorCommandEmun.放货完成, null, productTaskCode);
                if (res.IsSuccess)
                {
                    msg = $"AGV任务完成，下发设备 {tmpDevice.Name} 信号 {agvReqType} 成功。";
                    await logisticSer.AddTaskLog(task.LogisticTaskId, Models.LogMessageType.成功, msg);
                    return;
                }
                await Task.Delay(50);
                continue;
            }
            if (!res.IsSuccess)
            {
                msg = $"AGV任务完成，下发设备 {tmpDevice.Name} 信号 {agvReqType} 异常," + res.Message;
                SysLogger.LogError(msg);
                await logisticSer.AddTaskLog(task.LogisticTaskId, Models.LogMessageType.错误, msg);
                return;
            }
        }

        /// <summary>
        /// 依据物流任务查找生产任务ID
        /// </summary>
        /// <param name="logisticTaskId"></param>
        /// <returns></returns>
        private static async Task<string> GetProductTaskCode(long? logisticTaskId)
        {
            var Db = AutofacContainerModule.GetService<ServiceDbContext>().SqlSugarClient;
            var productTask = await Db.Set<Mds_LogisticTask>()
                .LeftJoin<Mds_ProductionTask>((a, b) => a.WorkOrder == b.WorkOrder)
                .Where((a, b) => a.LogisticTaskId == logisticTaskId)
                .Select((a, b) => b.ProductionTaskId).FirstOrDefaultAsync();

            return productTask.ToString();
        }

        /// <summary>
        /// 处理SCADA事件
        /// </summary>
        public static async Task HandleScadaEvent(ReqSendDeviceEvent data)
        {
            var taskCache = TaskCacheList.Where(x => x.DeviceCode == data.DeviceCode)
                .OrderByDescending(x => x.CreateTime).FirstOrDefault();
            if (taskCache == null)
            {
                var msg = "收到SCADA设备事件数据，但未匹配到对应的AGV任务";
                SysLogger.LogError(msg);
                throw new AppException(msg);
            }
            // 设备端已经通知对接完成
            taskCache.Processing = 2;
        }

        /// <summary>
        /// 根据点位获取关联的数采设备
        /// </summary>
        /// <param name="pos"></param>
        /// <returns></returns>
        private static RealDevice GetPosBindRealDevice(long? machineId, string pos)
        {

            MdsInit mdsInit = AutofacContainerModule.GetService<MdsInit>();
            RealDevice device = null;
            var posAlias = GetLogisticSiteAliasInfo(machineId, pos, null);
            if (posAlias != null)
            {
                device = mdsInit.devices.FirstOrDefault(x => x.Id == posAlias.RealDeviceId.ToString());
            }
            return device;
        }

        private static Mds_LogisticSiteAlias GetLogisticSiteAliasInfo(long? machineId, string? siteCodeAlias, string? confirmSteCode = null)
        {
            ISqlSugarClient Db = AutofacContainerModule.GetService<ServiceDbContext>().SqlSugarClient;
            var posAlias = Db.Set<Mds_LogisticSiteAlias>()
                .WhereIF(machineId != null, x => x.MachineId == machineId)
                .WhereIF(!string.IsNullOrEmpty(siteCodeAlias), x => x.SiteCodeAlias == siteCodeAlias)
                .WhereIF(!string.IsNullOrEmpty(confirmSteCode), x => x.ConfirmSiteCode == confirmSteCode)
                .FirstOrDefault();
            return posAlias;
        }

        /// <summary>
        /// AGV点位或路径控制做的任务缓存
        /// </summary>
        /// <param name="taskNo"></param>
        /// <param name="deviceCode"></param>
        /// <returns></returns>
        private static TaskCacheItem GetTaskCache(int taskNo, string deviceCode)
        {
            var item = TaskCacheList.FirstOrDefault(x => x.TaskNo == taskNo && x.DeviceCode == deviceCode);
            if (item == null)
            {
                item = new TaskCacheItem { TaskNo = taskNo, DeviceCode = deviceCode, Processing = 0 };
                TaskCacheList.Add(item);
            }
            return item;
        }
    }

    public class CreateLsAgvTaskDto
    {
        public AgvCommandEmun Command { get; set; }
        public int TaskNo { get; set; }
        public string Start { get; set; }
        public string End { get; set; }
        public RealDeviceTypeEmun DeviceType { get; set; }
        public string DeviceIp { get; set; }
    }

    public class WaitPositionItem
    {
        public string Pos { get; set; } = "";
        public string WaitingPos { get; set; } = "";
    }

    public class TaskCacheItem
    {
        public int TaskNo { get; set; }
        public string DeviceCode { get; set; }
        public int Processing { get; set; }
        public DateTime CreateTime { get; set; } = DateTime.Now;
    }
}
